/**
  ******************************************************************************
  * @file    DCMI/OV7670_Camera/dcmi_OV7670.c
  * @author  MCD Application Team
  * @version V1.0.0
  * @date    18-April-2011
  * @brief   This file includes the driver for OV7670 Camera module mounted on 
  *          STM322xG-EVAL board RevA and RevB.
  ******************************************************************************
  * @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
  ******************************************************************************
  */ 

/* Includes ------------------------------------------------------------------*/
#include "camera/dcmi_OV7670.h"
#include "camera/DCMI_OV7670_INITTABLE.h"

// this is in protocol :( 
uint8_t frame_bufferA[frame_size];
uint8_t frame_bufferB[frame_size];
// why the hell this does not work idk
//u32 *frameBufferADDR = (void*)&frame_bufferB[0];


#include "zFiles/main.h"


/** @addtogroup DCMI_OV7670_Camera
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Bits definitions ----------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
static void Delay(uint32_t nTime); 
static void Delay_ms(uint32_t nTime);


void DCMI_SnapShot(void)
{
	DMA_InitTypeDef  DMA_InitStructure;

	/* DMA2 Stream1 Configuration */  
	DMA_DeInit(DMA2_Stream1);
	
	DMA_InitStructure.DMA_Channel = DMA_Channel_1;  
	DMA_InitStructure.DMA_PeripheralBaseAddr = DCMI_DR_ADDRESS;
	if(mygVars->VideoStat == 1)
	{	
		DMA_InitStructure.DMA_Memory0BaseAddr =  (uint32_t)frame_bufferA;
	}
	if(mygVars->VideoStat == 2)
	{	
		DMA_InitStructure.DMA_Memory0BaseAddr =  (uint32_t)frame_bufferB;
	}
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
	DMA_InitStructure.DMA_BufferSize = sizeof(frame_bufferA) / sizeof(u32); // since it is doing it in 32 bit words 
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Word;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//DMA_Mode_Normal;//DMA_Mode_Circular;
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
	 
	DMA_Init(DMA2_Stream1, &DMA_InitStructure);

	DMA_Cmd(DMA2_Stream1, ENABLE);
	/* Enable DCMI interface */
	DCMI_Cmd(ENABLE);
	
	// enable this cropping feature
	//DCMI_CROPCmd(ENABLE); 
	
	/* Start Image capture */ 
	//DCMI_CaptureCmd(ENABLE);
	
	DCMI_CaptureCmd(ENABLE); 
}

/**
  * @brief  Configures the DCMI to interface with the OV7670 camera module.
  * @param  None
  * @retval None
  */
void DCMI_Config(void)
{
  DCMI_InitTypeDef DCMI_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
  DMA_InitTypeDef  DMA_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
  DCMI_CROPInitTypeDef DCMI_CROPInitStructure;
  
  /* Enable DCMI GPIOs clocks */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOE, ENABLE); 

  /* Enable DCMI clock */
  RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI, ENABLE);

  /* Connect DCMI pins to AF13 ************************************************/
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_DCMI);//HSYNC 
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_DCMI);//PCLK

  GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_DCMI);
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_DCMI);

  GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_DCMI);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_DCMI);//VSYNC
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_DCMI);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_DCMI);

  GPIO_PinAFConfig(GPIOE, GPIO_PinSource0, GPIO_AF_DCMI);
  GPIO_PinAFConfig(GPIOE, GPIO_PinSource1, GPIO_AF_DCMI);
  GPIO_PinAFConfig(GPIOE, GPIO_PinSource4, GPIO_AF_DCMI);
  
  /* DCMI GPIO configuration **************************************************/
    /*
  D0 -- PC6
  D1 -- PC7
  D2 -- PE0
  D3 -- PE1
  D4 -- PE4
  D5 -- PB6
  D6 -- PB8
  D7 -- PB9
  PCK - PA6
  HS -- PA4
  VS -- PB7
  XCLK -- PA8
  */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;  
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
  GPIO_Init(GPIOB, &GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4;
  GPIO_Init(GPIOE, &GPIO_InitStructure);

  /* PCLK(PA6) */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  /* DCMI configuration *******************************************************/ 
  DCMI_InitStructure.DCMI_CaptureMode = DCMI_CaptureMode_SnapShot;//DCMI_CaptureMode_Continuous DCMI_CaptureMode_SnapShot;
  DCMI_InitStructure.DCMI_SynchroMode = DCMI_SynchroMode_Hardware;
  DCMI_InitStructure.DCMI_PCKPolarity = DCMI_PCKPolarity_Rising;
  DCMI_InitStructure.DCMI_VSPolarity = DCMI_VSPolarity_High;
  DCMI_InitStructure.DCMI_HSPolarity = DCMI_HSPolarity_High;
  DCMI_InitStructure.DCMI_CaptureRate = DCMI_CaptureRate_All_Frame;//DCMI_CaptureRate_1of4_Frame;
  DCMI_InitStructure.DCMI_ExtendedDataMode = DCMI_ExtendedDataMode_8b;
  
  DCMI_Init(&DCMI_InitStructure);

  /* DCMI Interrupts config ***************************************************/
  DCMI_ITConfig(DCMI_IT_VSYNC, ENABLE);
  DCMI_ITConfig(DCMI_IT_LINE, ENABLE);
  DCMI_ITConfig(DCMI_IT_FRAME, ENABLE);
  DCMI_ITConfig(DCMI_IT_ERR, ENABLE);
  //DCMI_ITConfig(DCMI_IT_OVF, ENABLE);
      
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  NVIC_InitStructure.NVIC_IRQChannel = DCMI_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
  NVIC_Init(&NVIC_InitStructure); 

  /* Configures the DMA2 to transfer Data from DCMI to the LCD ****************/
  /* Enable DMA2 clock */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);  
  
  /* DMA2 Stream1 Configuration */  
  DMA_DeInit(DMA2_Stream1);

  DMA_InitStructure.DMA_Channel = DMA_Channel_1;  
  DMA_InitStructure.DMA_PeripheralBaseAddr = DCMI_DR_ADDRESS;	
  DMA_InitStructure.DMA_Memory0BaseAddr =  (uint32_t)frame_bufferA;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
  DMA_InitStructure.DMA_BufferSize = sizeof(frame_bufferA) / sizeof(u32); // since it is doing it in 32 bit words 
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Word;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//DMA_Mode_Normal;//DMA_Mode_Circular;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;         
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
     
  DMA_Init(DMA2_Stream1, &DMA_InitStructure); 

    // only get the upper left corner, a small peice of the image
  DCMI_CROPInitStructure.DCMI_VerticalStartLine = 0; 
  DCMI_CROPInitStructure.DCMI_VerticalLineCount = frame_crop_height;
  DCMI_CROPInitStructure.DCMI_HorizontalOffsetCount = 0;
  DCMI_CROPInitStructure.DCMI_CaptureCount = frame_crop_width*2;
  // config the crop 
  DCMI_CROPConfig(&DCMI_CROPInitStructure);

}

/**
  * @brief  Set PA8 Output SYSCLK/2.
  * @param  None
  * @retval None
  */
void MCO1_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_ClockSecuritySystemCmd(ENABLE);

	/* Enable GPIOs clocks */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_MCO);
		
	/* Configure MCO (PA8) */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	RCC_MCO1Config(RCC_MCO1Source_PLLCLK, RCC_MCO1Div_3);
}

void DCMI_0V7670_PWDN_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	/* Enable GPIOs clocks */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
		
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	/*PWDN*/
	GPIO_ResetBits(GPIOA, GPIO_Pin_2);
	Delay_ms(10);
	GPIO_SetBits(GPIOA, GPIO_Pin_2);	
}

uint8_t writeRegs(regval_list *vals)
{
	Delay(500);
	while (vals->reg_num != 0xff || vals->value != 0xff) {
		int ret = DCMI_SingleRandomWrite( vals->reg_num, vals->value);
		if (ret < 0)
		{
			printf("OV7670 Fail while writing cam reg: 0x%X val: 0x%X \r\n",vals->reg_num,vals->value);
			return ret;
		}
		vals++;
	}
	Delay(500);
	return 0;
}

/**
  * @brief  Set the VGA size(640*320).
  * @param  None
  * @retval None
  */
uint8_t DCMI_OV7670_Init(void)
{	
	uint8_t i;
	SCCB_GPIO_Config();
	DCMI_Config();
	MCO1_Init();
	//DCMI_0V7670_RST_PWDN_Init();
			
  	Delay_ms(0xfff);
	if(DCMI_SingleRandomWrite(OV7670_COM7, SCCB_REG_RESET)!=0)
		return 0xff;
	Delay_ms(0xfff);
  
	for(i=0;i<CHANGE_REG_NUM;i++)
	{
		if(DCMI_SingleRandomWrite(change_reg[i][0],change_reg[i][1])!=0)
		{
			return 0xff;
		}
	}

//	if(writeRegs(ov7670_default_regs)!=0)
//	{
//		return false;
//	}

	if(writeRegs(ov7670_qcif_regs)!=0)
	{
		return false;
	}

	if(writeRegs(ov7670_fmt_rgb565)!=0)
	{
		return false;
	}

	//OV7670_config_window(272,16,176,144);
  	
	return 0;//Init ok
}


void InitCamera(void)
{
	OV7670_IDTypeDef OV7670ID;
	RCC_ClocksTypeDef SYS_Clocks;
	//USART_NVIC_Config();

	if (SysTick_Config(SystemCoreClock / 1000))
  	{ 
	    /* Capture error */ 
	    while (1);
  	}
	RCC_GetClocksFreq(&SYS_Clocks);

	printf("\r\nSYSCLK:%dM\r\n",SYS_Clocks.SYSCLK_Frequency/1000000);
	printf("HCLK:%dM\r\n",SYS_Clocks.HCLK_Frequency/1000000);
	printf("PCLK1:%dM\r\n",SYS_Clocks.PCLK1_Frequency/1000000);
	printf("PCLK2:%dM\r\n",SYS_Clocks.PCLK2_Frequency/1000000);

	Delay(1000);
	//GUI_Text(100,104,"Camera Init..",White,Blue);


	printf("OV7670 Init..\r\n");
	if(DCMI_OV7670_Init()==0){

		printf("Camera Have Init..\r\n");
				DCMI_OV7670_ReadID(&OV7670ID);
		if(DCMI_OV7670_ReadID(&OV7670ID)==0)
		{	
			if(OV7670ID.Manufacturer_ID1==0x7f || OV7670ID.Manufacturer_ID2==0xa2 ||OV7670ID.Version==0x73 ||OV7670ID.PID==0x76)
				printf("OV7670 ID:0x%x 0x%x 0x%x 0x%x\r\n",OV7670ID.Manufacturer_ID1, OV7670ID.Manufacturer_ID2, OV7670ID.PID, OV7670ID.Version);
			else
				printf("OV7670 ID is Error!\r\n");
		}
																											  
		Delay(1000);
			   
		if(DCMI_SingleRandomWrite(0x71, 0x80)==0) //color test	on
			printf("color test on..\r\n");
		else
			printf("color test on fails!!\r\n");

		if(DCMI_SingleRandomWrite(0x71, 0x00)==0) //color test off
			printf("color test off..\r\n");
		else
			printf("color test off fails!!\r\n");

		Delay(1000);
			    /* Enable DMA transfer */
		DMA_Cmd(DMA2_Stream1, ENABLE);
			/* Enable DCMI interface */
		DCMI_Cmd(ENABLE);
		
		// enable this cropping feature
  		//DCMI_CROPCmd(ENABLE); 
		
		/* Start Image capture */ 
		DCMI_CaptureCmd(ENABLE);

		Delay(1000);

		}
	else 
	{
		printf("OV7670 Init fails!!\r\n");
		//GUI_Text(100,120,"OV7670 Init fails!!",White,Blue);
		mygVars->status |= ERR_CAM;
	}





}

/**
  * @brief  Read the OV7670 Manufacturer identifier.
  * @param  OV7670ID: pointer to the OV7670 Manufacturer identifier. 
  * @retval None
  */
uint8_t DCMI_OV7670_ReadID(OV7670_IDTypeDef* OV7670ID)
{
	uint8_t temp;
	if(DCMI_SingleRandomRead(OV7670_MIDH,&temp)!=0)
		return 0xff;
	OV7670ID->Manufacturer_ID1 = temp;
	if(DCMI_SingleRandomRead(OV7670_MIDL,&temp)!=0)
		return 0xff;
	OV7670ID->Manufacturer_ID2 = temp;
	if(DCMI_SingleRandomRead(OV7670_VER,&temp)!=0)
		return 0xff;
	OV7670ID->Version = temp;
	if(DCMI_SingleRandomRead(OV7670_PID,&temp)!=0)
		return 0xff;
	OV7670ID->PID = temp;

	return 0;
}
/**
  * @brief  config_OV7660_window 
  * @param  //(140,16,640,480) is good for VGA
  *			//(272,16,320,240) is good for QVGA
  * @retval None
  */
void OV7670_config_window(uint16_t startx, uint16_t starty, uint16_t width, uint16_t height)
{
	uint16_t endx=(startx+width);
	uint16_t endy=(starty+height*2);// must be "height*2"
	uint8_t temp_reg1, temp_reg2;
	uint8_t state,temp;
	
	state = state;	   //Prevent report warning

	state = DCMI_SingleRandomRead(0x03, &temp_reg1 );
	temp_reg1 &= 0xC0;
	state = DCMI_SingleRandomRead(0x32, &temp_reg2 );
	temp_reg2 &= 0xC0;
	
	// Horizontal
	temp = temp_reg2|((endx&0x7)<<3)|(startx&0x7);
	state = DCMI_SingleRandomWrite(0x32, temp );
	temp = (startx&0x7F8)>>3;
	state = DCMI_SingleRandomWrite(0x17, temp );
	temp = (endx&0x7F8)>>3;
	state = DCMI_SingleRandomWrite(0x18, temp );
	
	// Vertical
	temp = temp_reg1|((endy&0x7)<<3)|(starty&0x7);
	state = DCMI_SingleRandomWrite(0x03, temp );
	temp = (starty&0x7F8)>>3;
	state = DCMI_SingleRandomWrite(0x19, temp );
	temp = (endy&0x7F8)>>3;
	state = DCMI_SingleRandomWrite(0x1A, temp );
}



 			 			  
/**
  * @}
  */
static void Delay(uint32_t nTime)
{
  while(nTime--);
} 

static void Delay_ms(uint32_t nTime)
{
  while(nTime--)
  	{Delay(1000);}

}

/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
